/**
 * @PROJECT 
 * @DATE 2018年7月10日 上午9:08:49
 * @AUTHOR LUYU
 */
package com.kool.tlwp.generator;

import java.io.File;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.kool.tlwp.bean.IoFieldVo;
import com.kool.tlwp.bean.OptionVo;
import com.kool.tlwp.bean.RuleVo;
import com.kool.tlwp.dao.GlobalDefineDao;
import com.kool.tlwp.exception.AppException;
import com.kool.tlwp.generator.context.GenerateContext;
import com.kool.tlwp.util.FileUtils;
import com.kool.tlwp.util.StringUtils;

import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;

/**
 * @DESCRIBE 代码生成工具
 * @AUTHOR LUYU
 * @DATE 2018年7月10日 上午9:08:49
 *
 */
public abstract class CodeGenerator {
	protected GenerateContext context;
	protected Configuration configuration;

	public CodeGenerator(GenerateContext context) throws AppException {
		this.context = context;
		try {
			// freemarker配置
			String templateDir = context.getTemplateDir();
			if (StringUtils.isEmpty(templateDir)) {
				templateDir = StringUtils.getProjectRoot()+"\\template";
			}
			configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
			configuration.setDirectoryForTemplateLoading(new File(templateDir));
			configuration.setDefaultEncoding("UTF-8");
			configuration.setObjectWrapper(new DefaultObjectWrapper(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS));

		} catch (IOException e) {
			throw new AppException("操作Template文件错误!", e);
		}
	}

	/**
	 * @DESCRIBE
	 * @DATE 2017年8月25日 下午11:12:23
	 *
	 * @param descDir
	 * @param pkg
	 * @return
	 */
	protected static String getEntityDir(String destDir, String pkg) {
		String dir = destDir + "\\" + pkg.replace('.', '\\') + "\\bean";
		File fDir = new File(dir);
		if (fDir.exists() == false) {
			fDir.mkdirs();
		}
		return dir;
	}

	/**
	 * @DESCRIBE
	 * @DATE 2017年8月25日 下午11:08:30
	 *
	 * @param descDir
	 * @param pkg
	 * @return
	 */
	protected static String getDaoDir(String destDir, String pkg) {
		String dir = destDir + "\\" + pkg.replace('.', '\\') + "\\dao";
		File fDir = new File(dir);
		if (fDir.exists() == false) {
			fDir.mkdirs();
		}
		return dir;
	}

	/**
	 * @DESCRIBE
	 * @DATE 2018年7月10日 上午9:19:17
	 *
	 * @param destDir
	 * @param pkg
	 * @return
	 */
	protected static String getServiceDir(String destDir, String pkg) {
		String dir = destDir + "\\" + pkg.replace('.', '\\') + "\\service";
		File fDir = new File(dir);
		if (fDir.exists() == false) {
			fDir.mkdirs();
		}
		return dir;
	}

	/**
	 * @DESCRIBE
	 * @DATE 2018年7月10日 上午9:19:13
	 *
	 * @param destDir
	 * @param pkg
	 * @param name
	 * @return
	 */
	protected static String getActionDir(String destDir, String pkg, String name) {
		String dir = destDir + "\\" + pkg.replace('.', '\\') + "\\action" + "\\" + name;
		File fDir = new File(dir);
		if (fDir.exists() == false) {
			fDir.mkdirs();
		}
		return dir;
	}

	/**
	 * @DESCRIBE
	 * @DATE 2018年7月10日 上午9:19:13
	 *
	 * @param destDir
	 * @param pkg
	 * @param name
	 * @return
	 */
	protected static String getIoDir(String destDir, String pkg, String name) {
		String dir = destDir + "\\" + pkg.replace('.', '\\') + "\\bean\\io" + "\\" + name;
		File fDir = new File(dir);
		if (fDir.exists() == false) {
			fDir.mkdirs();
		}
		return dir;
	}

	/**
	 * 
	 * @DESCRIBE
	 * @DATE 2018年7月10日 下午7:31:15
	 *
	 * @param destDir
	 * @param lowName
	 * @return
	 */
	protected static String getVueDir(String destDir, String lowName) {
		String dir = destDir + "\\" + lowName;
		File fDir = new File(dir);
		if (fDir.exists() == false) {
			fDir.mkdirs();
		}
		return dir;
	}

	/**
	 * @DESCRIBE
	 * @DATE 2017-04-29 16:20:52
	 *
	 * @return
	 * @throws AppException
	 */
	protected Connection getConnection() throws AppException {
		Connection connection = null;
		try {
			String sUrl = context.getUrl();
			String sNam = context.getName();
			String sPwd = context.getPwd();
			String sDriver = context.getDriver();

			Class.forName(sDriver);

			connection = DriverManager.getConnection(sUrl, sNam, sPwd);
		} catch (ClassNotFoundException e) {
			StringUtils.debug("Create connection failed :" + e.getMessage());
			throw new AppException("Create connection failed :" + e.getMessage());
		} catch (SQLException e) {
			StringUtils.debug("Create connection failed :" + e.getMessage());
			throw new AppException("Create connection failed :" + e.getMessage());
		}
		return connection;
	}

	/**
	 * @DESCRIBE
	 * @DATE 2018年7月10日 上午9:44:15
	 *
	 * @param ioName
	 * @return
	 * @throws Exception
	 */
	protected List<IoFieldVo> json2IoVoList(String ioName) throws Exception {
		String ioFile = context.getIoDir();
		ioFile = ioFile + "\\" + ioName + ".json";
		String ioJson = FileUtils.readFile(ioFile, "utf-8");
		JSONArray ja = JSON.parseArray(ioJson);
		List<IoFieldVo> list = new ArrayList<>();
		for (Object object : ja) {
			JSONObject jo = (JSONObject) object;
			IoFieldVo vo = new IoFieldVo();
			vo.setIoName(ioName);
			vo.setJavaType(transferType(jo.getString("javaType")));
			String fieldName = jo.getString("fieldName");
			vo.setFieldName(fieldName);
			vo.setLowJavaName(fieldName);
			String upName = fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
			vo.setUpJavaName(upName);
			vo.setChnName(jo.getString("chnName"));
			String globalDefine = jo.getString("globalDefine");
			vo.setGlobalDefine(globalDefine);
			String type = jo.getString("type");
			vo.setType(type);
			if (StringUtils.isEmpty(type)==false && "select".equals(type)) {
				List<OptionVo> listOption = getOptions(globalDefine);
				vo.setSelectItems(listOption);
			}
			//与后台校验模板匹配
			vo.setNullable(jo.getString("nullable"));
			vo.setMaxLen(jo.getInteger("maxLen"));
			vo.setMinLen(jo.getInteger("minLen"));
			vo.setMax(jo.getInteger("max"));
			vo.setMin(jo.getInteger("min"));

			//与前台校验模板匹配
			List<RuleVo> rules = new ArrayList<>();
			if (jo.getString("nullable") !=null) {
				RuleVo rule = new RuleVo();
				rule.setType("nullable");
				rule.setValue(jo.getString("nullable"));
				rules.add(rule);
			}
			if (jo.getInteger("maxLen")!= null || jo.getInteger("minLen") != null ) {
				RuleVo rule = new RuleVo();
				rule.setType("length");
				rule.setMaxLen(jo.getInteger("maxLen"));
				rule.setMinLen(jo.getInteger("minLen"));
				rules.add(rule);
			}
			if ("Integer".equals(jo.getString("javaType"))) {
				RuleVo rule = new RuleVo();
				rule.setType("number");
				rules.add(rule);
				if (jo.getString("max")!= null ||jo.getInteger("min") != null) {
					RuleVo ruleRng = new RuleVo();
					ruleRng.setType("range");
					ruleRng.setMax(jo.getInteger("max"));
					ruleRng.setMin(jo.getInteger("min"));
					rules.add(ruleRng);
				}
			}
			vo.setRules(rules);
			vo.setIsPK(jo.getString("isPK"));
			list.add(vo);
		}
		return list;
	}
	
	protected static String transferType(String dataType) {
		String type = null;
		if ("VARCHAR".equalsIgnoreCase(dataType)
				|| ("CHAR".equalsIgnoreCase(dataType))
				|| ("STRING".equalsIgnoreCase(dataType))) {
			type = "String";
		} else if ("INT".equalsIgnoreCase(dataType)
				|| ("INTEGER".equalsIgnoreCase(dataType))) {
			type = "Integer";
		} else if ("DOUBLE".equalsIgnoreCase(dataType)) {
			type = "Double";
		} else if ("DATE".equalsIgnoreCase(dataType)) {
			type = "java.sql.Date";
		} else if ("TIMESTAMP".equalsIgnoreCase(dataType)) {
			type = "java.sql.Timestamp";
		} else if ("DECIMAL".equalsIgnoreCase(dataType)
				|| ("BIGDECIMAL".equalsIgnoreCase(dataType))) {
			type = "java.math.BigDecimal";
		} else{
			type = "Object";
		}
		return type;
	}

	/**
	 * @DESCRIBE
	 * @DATE 2018年7月10日 下午7:57:15
	 *
	 * @param globalDefine
	 * @return
	 * @throws AppException
	 */
	private List<OptionVo> getOptions(String globalDefine) throws AppException {
		Connection connection = getConnection();
		GlobalDefineDao dao = new GlobalDefineDao(connection);
		List<OptionVo> list = null;
		try {
			list = dao.select(globalDefine);
		} catch (SQLException e) {
			throw new AppException("查询系统预定义时，发生数据库异常！", e);
		}

		return list;
	}

	/**
	 * 
	 * @DESCRIBE 生成文件
	 * @DATE 2018年7月10日 上午9:13:12
	 *
	 * @throws AppException
	 */
	public abstract void generate() throws AppException;
}
